package com.example.lockdemo.handle;

import cn.hutool.extra.spring.SpringUtil;
import com.example.lockdemo.config.MyLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * @author GuoAo
 * @className LockHander
 * @date 2025/4/24
 * @description TODO
 */
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class LockHandler {
    private final RedissonClient client;

    private final ThreadLocal<RLock> lock = new ThreadLocal<>();

    @Around(value = "@annotation(mylock)")
    public Object round(ProceedingJoinPoint pjp, MyLock mylock) throws Throwable {

        String key = mylock.key();
        String s = parseValue(key, pjp);
        log.info("key: {}", s);
        getLock(s);

        RLock rLock = lock.get();
        if(rLock.tryLock(mylock.waitTime(), TimeUnit.SECONDS)){
            log.info("获取到锁的时间：{}",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(LocalDateTime.now()));
            Object proceed = pjp.proceed();
            rLock.unlock();
            setSuccessCount(s);
            return proceed;
        }else {
            throw new RuntimeException("获取锁失败请稍后再试！");
        }

    }

    private void setSuccessCount(String key){
        String date = DateTimeFormatter
                .ofPattern("yyyy-MM-dd").format(LocalDateTime.now());
        RAtomicLong atomicLong = client.getAtomicLong(String.format("%s:%s", date, key));
        atomicLong.getAndAdd(1);
    }

    private void  getLock(String key){
        RLock lock1 = client.getLock(key);
        lock.set(lock1);
    }

    private String parseValue(String key, ProceedingJoinPoint pjp){
        ExpressionParser parser = new SpelExpressionParser();
        ParserContext parserContext = new TemplateParserContext();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        // Spring的Bean对象解析器
        BeanResolver resolver = new BeanFactoryResolver(SpringUtil.getBeanFactory());
        StandardEvaluationContext context = new StandardEvaluationContext();
        // 赋予上下文解析器解析Bean对象的能力
        context.setBeanResolver(resolver);
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Object[] args = pjp.getArgs();
        for (int i = 0; i < args.length; i++){
            assert parameterNames != null;
            log.info("agrName: {},agr: {}", parameterNames[i], args[i]);
            context.setVariable(parameterNames[i], args[i]);
        }
        Expression expression = null;
        // * 如果是#{} 表示时计算表达式 也可以是其他方法的调用 getExpressionPrefix()-> #{   getExpressionSuffix()-> }
        if(key.startsWith(parserContext.getExpressionPrefix()) && key.endsWith(parserContext.getExpressionSuffix())){
            expression = parser.parseExpression(key, parserContext);
        }else{
            // * # 开头的是直接解析属性
            expression = parser.parseExpression(key);
        }
        return expression.getValue(context, String.class);

    }

    @AfterThrowing(value = "@annotation(myLock)")
    public void afterThrowing(JoinPoint joinPoint, MyLock myLock) {
        RLock rLock = lock.get();
        rLock.unlock();
        lock.remove();
    }


}
